#include "MainWindow.h"
#include <QFileDialog>
#include <fstream>
#include <memory>
#include <QDebug>
#include <QMessageBox>
#include <QStyle>
#include "Util.h"

static void GetImageFormMcs(Mcs& src, cv::Mat& dst, int frameId);
static void LoadFromArgs(Mcs& mcs, const McsImportArgs& args);

MainWindow* MainWindow::instance = nullptr;

MainWindow* MainWindow::Instance()
{
    if(instance == nullptr)
        instance = new MainWindow();
    return instance;
}

MainWindow::MainWindow() : QMainWindow()
{
    menuBar() -> setFixedWidth(size().width());

    fileMenu = std::make_unique<QMenu>("文件", this);
    fileOpenAction = std::make_unique<QAction>("打开", this);
    fileSaveAction = std::make_unique<QAction>("保存", this);
	fileImportAction = std::make_unique<QAction>("导入", this);
    fileQuitAction = std::make_unique<QAction>("退出", this);
    fileOpenAction -> setIcon(style() -> standardIcon(QStyle::StandardPixmap::SP_DialogOpenButton));
    fileSaveAction -> setIcon(style() -> standardIcon(QStyle::StandardPixmap::SP_DialogSaveButton));
    fileImportAction -> setIcon(style() -> standardIcon(QStyle::StandardPixmap::SP_ArrowDown));
    fileQuitAction -> setIcon(style() -> standardIcon(QStyle::StandardPixmap::SP_DialogCloseButton));

    fileMenu -> addAction(fileOpenAction.get());
    fileMenu -> addAction(fileSaveAction.get());
    fileMenu -> addAction(fileImportAction.get());
    fileMenu -> addAction(fileQuitAction.get());
    menuBar() -> addMenu(fileMenu.get());

    toolBar = addToolBar("快捷栏");
    toolBar -> setFloatable(false);
    toolBar -> setMovable(false);
    toolBar -> addAction(fileOpenAction.get());
    toolBar -> addAction(fileSaveAction.get());
    toolBar -> addAction(fileImportAction.get());

    statusText = new QLabel(this);
    statusBar() -> addWidget(statusText);

    mainWidget = new QWidget(this);
    mainLayout = new QHBoxLayout(mainWidget);
    mainWidget -> setLayout(mainLayout);
    setCentralWidget(mainWidget);

    controlPanel = new ControlPanel(this);
    controlPanel -> setFixedWidth(300);
    frameShower = new FrameShower(this);
    mainLayout -> addWidget(controlPanel);
    mainLayout -> addWidget(frameShower);

    setWindowTitle("Mcs 导入器");
    SetStatusText("状态栏信息");
    setMinimumSize(850, 600);

    connect(this, SIGNAL(McsUpdate()), controlPanel, SLOT(OnMcsUpdate()));
    connect(this, SIGNAL(McsUpdate()), frameShower, SLOT(OnMcsUpdate()));
    connect(fileOpenAction.get(), SIGNAL(triggered()), this, SLOT(OnFileOpenActionCalled()));
	connect(fileSaveAction.get(), SIGNAL(triggered()), this, SLOT(OnFileSaveActionCalled()));
	connect(fileImportAction.get(), SIGNAL(triggered()), this, SLOT(OnFileImportActionCalled()));
    connect(fileQuitAction.get(), SIGNAL(triggered()), this, SLOT(OnFileQuitActionCalled()));
    connect(controlPanel, SIGNAL(StatusUpdate(const QColor&, int)), this, SLOT(UpdateImage(const QColor&, int)));
}

MainWindow::~MainWindow()
{
    delete statusText;
    delete mainLayout;
    delete mainWidget;
    delete controlPanel;
}

void MainWindow::resizeEvent(QResizeEvent *event)
{
    menuBar() -> setFixedWidth(size().width());
    QWidget::resizeEvent(event);
}

void MainWindow::SetStatusText(const QString &text)
{
    statusText -> setText(text);
}

std::unique_ptr<QImage> MainWindow::GetMcsImage(int frameId)
{
	cv::Mat image = cv::Mat(cv::Size(mcs->GetMcsHead().GetWidth(), mcs->GetMcsHead().GetHeight()), CV_8UC3, cv::Scalar(backgroundColor.blue(), backgroundColor.green(), backgroundColor.red()));
	GetImageFormMcs(*mcs, image, frameId);
    return Util::CvImage2QImage(image);
}

std::shared_ptr<Mcs> MainWindow::GetMcs()
{
    return mcs;
}

void MainWindow::OnFileOpenActionCalled()
{
    QString fileName = QFileDialog::getOpenFileName(this, tr("打开Mcs文件"), "", tr("Mcs文件(*.mcs)"));
	try
	{
		if(fileName.isEmpty())
		{
            return;
		}
		std::ifstream i(fileName.toStdString(), std::ios::binary);
		mcs = std::make_unique<Mcs>();
		mcs->TryLoad(i);
		emit(McsUpdate());
	}
	catch (const StrException& e)
	{
		QMessageBox::warning(this, "打开文件时发生了一个错误", e.what());
	}
	catch (...)
	{
		QMessageBox::warning(this, "异常", "未知错误");
	}
}

void MainWindow::OnFileSaveActionCalled()
{
    QString fileName = QFileDialog::getSaveFileName(this, tr("保存Mcs文件"), "", tr("Mcs文件(*.mcs)"));

	try
	{
		if(fileName.isEmpty())
		{
            return;
		}
		if(mcs == nullptr)
		{
			throw StrException("无已打开的Mcs文件");
		}
		std::ofstream o(fileName.toStdString(), std::ios::binary);
		mcs -> TryDump(o);
	}
	catch (const StrException& e)
	{
		QMessageBox::warning(this, "保存文件时发生了一个错误", e.what());
	}
	catch (...)
	{
		QMessageBox::warning(this, "异常", "未知错误");
	}

}

void MainWindow::OnFileImportActionCalled()
{
	ImportDialog dialog(this);
	int result = dialog.exec();
	if(result == QDialog::Accepted)
	{
		try
		{
			mcs = std::make_shared<Mcs>();
			LoadFromArgs(*mcs, dialog.GetArgs());
			emit(McsUpdate());
		}
		catch (const StrException& e)
		{
			QMessageBox::warning(this, "导入时发生了一个错误", e.what());
		}
		catch (...)
		{
			QMessageBox::warning(this, "异常", "未知错误");
		}
	}
}

void MainWindow::OnFileQuitActionCalled()
{
    close();
}

void MainWindow::UpdateImage(const QColor &c, int index)
{
	backgroundColor = c;
	frameShower -> SetFrame(GetMcsImage(index));
}

void GetImageFormMcs(Mcs& src, cv::Mat& dst, int frameId)
{
	auto fp = src.GetFrames()[frameId];
	if(src.GetMcsHead().IsWithShadow())
	{
		int shadowIndex = -1;
		for(int i = 0; i < fp->GetMultChannel().size(); i++)
		{
			if(fp->GetMultChannel()[i]->GetMultChannelHead().GetMCType() == MCType::Shadow)
			{
				shadowIndex = i;
				break;
			}
		}
		if(shadowIndex == -1)
			throw StrException("File Error!");
		cv::Mat shadowCanvas = dst(cv::Rect(fp->GetMultChannel()[shadowIndex]->GetMultChannelHead().GetX(),
											fp->GetMultChannel()[shadowIndex]->GetMultChannelHead().GetY(),
											fp->GetMultChannel()[shadowIndex]->GetMultChannelHead().GetW(),
											fp->GetMultChannel()[shadowIndex]->GetMultChannelHead().GetH()));
		cv::Mat shadow(
				fp->GetMultChannel()[shadowIndex]->GetMultChannelHead().GetH(),
				fp->GetMultChannel()[shadowIndex]->GetMultChannelHead().GetW(),
				CV_8UC3,
				cv::Scalar(0, 0, 0));
		cv::Mat a(
				fp->GetMultChannel()[shadowIndex]->GetMultChannelHead().GetH(),
				fp->GetMultChannel()[shadowIndex]->GetMultChannelHead().GetW(),
				CV_8UC1,
				fp->GetMultChannel()[shadowIndex]->GetChannels()[0]);
		Util::DrawImageOnCanvas(shadowCanvas, shadow, a);
	}

	int bodyIndex = -1;
	for(int i = 0; i < fp->GetMultChannel().size(); i++)
	{
		if(fp->GetMultChannel()[i]->GetMultChannelHead().GetMCType() == MCType::Body)
		{
			bodyIndex = i;
			break;
		}
	}
	if(bodyIndex == -1)
		throw StrException("File Error!");
	cv::Mat body = dst(cv::Rect(fp->GetMultChannel()[bodyIndex]->GetMultChannelHead().GetX(),
								fp->GetMultChannel()[bodyIndex]->GetMultChannelHead().GetY(),
								fp->GetMultChannel()[bodyIndex]->GetMultChannelHead().GetW(),
								fp->GetMultChannel()[bodyIndex]->GetMultChannelHead().GetH()));
	cv::Mat tmp[3], a, i;
	tmp[0] = cv::Mat(
			fp->GetMultChannel()[bodyIndex]->GetMultChannelHead().GetH(),
			fp->GetMultChannel()[bodyIndex]->GetMultChannelHead().GetW(),
			CV_8UC1,
			fp->GetMultChannel()[bodyIndex]->GetChannels()[2]);
	tmp[1] = cv::Mat(
			fp->GetMultChannel()[bodyIndex]->GetMultChannelHead().GetH(),
			fp->GetMultChannel()[bodyIndex]->GetMultChannelHead().GetW(),
			CV_8UC1,
			fp->GetMultChannel()[bodyIndex]->GetChannels()[1]);
	tmp[2] = cv::Mat(
			fp->GetMultChannel()[bodyIndex]->GetMultChannelHead().GetH(),
			fp->GetMultChannel()[bodyIndex]->GetMultChannelHead().GetW(),
			CV_8UC1,
			fp->GetMultChannel()[bodyIndex]->GetChannels()[0]);
	a = cv::Mat(
			fp->GetMultChannel()[bodyIndex]->GetMultChannelHead().GetH(),
			fp->GetMultChannel()[bodyIndex]->GetMultChannelHead().GetW(),
			CV_8UC1,
			fp->GetMultChannel()[bodyIndex]->GetChannels()[3]);
	cv::merge(tmp, 3, i);
	Util::DrawImageOnCanvas(body, i, a);
}

void LoadFromArgs(Mcs& mcs, const McsImportArgs& args)
{
	char fileTmp[100];
	cv::Mat imgTmp;

	cv::Rect rect;
	cv::Mat tmp[4];
	cv::Mat newTmp[4];

	mcs.SetMcsHead(McsHead(args.width, args.height, args.frameCount, args.isWithShadow, args.isWithRemap));

    int fileSeqOffset = args.isFileNameFormZero ? 0 : 1;

	for(int i = 0; i < mcs.GetMcsHead().GetFrameCount(); ++i)
	{
		mcs.GetFrames()[i]->SetMcsHeadPointer(mcs.GetMcsHead());
		sprintf(fileTmp, args.bodyFileName.toStdString().c_str(), i + fileSeqOffset);
		imgTmp = cv::imread((args.bodyPath + fileTmp).toStdString(), cv::IMREAD_UNCHANGED);
		cv::split(imgTmp, tmp);
		rect = cv::boundingRect(tmp[3]);
        {
            int maxX = 0, maxY = 0;
            int cX = args.width / 2, cY = args.height / 2;
            int x1 = fabs(cX - rect.x);
            int x2 = fabs(cX - rect.x - rect.width);
            maxX = x1 > x2 ? x1 : x2;
            int y1 = fabs(cY - rect.y);
            int y2 = fabs(cY - rect.y - rect.height);
            maxY = y1 > y2 ? y1 : y2;
            rect.x = cX - maxX;
            rect.y = cY - maxY;
            rect.width = 2 * maxX;
            rect.height = 2 * maxY;
        }

//		rect = cv::Rect(0, 0, args.width, args.height);

        tmp[0](rect).copyTo(newTmp[0]);
        tmp[1](rect).copyTo(newTmp[1]);
        tmp[2](rect).copyTo(newTmp[2]);
        tmp[3](rect).copyTo(newTmp[3]);

		mcs.GetFrames()[i]->GetMultChannel()[0]->SetMultChannel(rect.x, rect.y, rect.width, rect.height, {newTmp[2].data, newTmp[1].data, newTmp[0].data, newTmp[3].data}, MCType::Body);

		if (args.isWithShadow)
		{
			sprintf(fileTmp, args.shadowFileName.toStdString().c_str(), i + fileSeqOffset);
			imgTmp = cv::imread((args.shadowPath + fileTmp).toStdString(), cv::IMREAD_UNCHANGED);
			cv::split(imgTmp, tmp);
			rect = cv::boundingRect(tmp[3]);
            {
                int maxX = 0, maxY = 0;
                int cX = args.width / 2, cY = args.height / 2;
                int x1 = fabs(cX - rect.x);
                int x2 = fabs(cX - rect.x - rect.width);
                maxX = x1 > x2 ? x1 : x2;
                int y1 = fabs(cY - rect.y);
                int y2 = fabs(cY - rect.y - rect.height);
                maxY = y1 > y2 ? y1 : y2;
                rect.x = cX - maxX;
                rect.y = cY - maxY;
                rect.width = 2 * maxX;
                rect.height = 2 * maxY;
            }
//            rect = cv::Rect(0, 0, args.width, args.height);
			tmp[3](rect).copyTo(newTmp[3]);
			mcs.GetFrames()[i]->GetMultChannel()[1]->SetMultChannel(rect.x, rect.y, rect.width, rect.height,{newTmp[3].data}, MCType::Shadow);
		}

		if(args.isWithRemap)
		{
			sprintf(fileTmp, args.remapFileName.toStdString().c_str(), i + fileSeqOffset);
			imgTmp = cv::imread((args.remapPath + fileTmp).toStdString(), cv::IMREAD_UNCHANGED);
			cv::split(imgTmp, tmp);
			rect = cv::boundingRect(tmp[3]);
            {
                int maxX = 0, maxY = 0;
                int cX = args.width / 2, cY = args.height / 2;
                int x1 = fabs(cX - rect.x);
                int x2 = fabs(cX - rect.x - rect.width);
                maxX = x1 > x2 ? x1 : x2;
                int y1 = fabs(cY - rect.y);
                int y2 = fabs(cY - rect.y - rect.height);
                maxY = y1 > y2 ? y1 : y2;
                rect.x = cX - maxX;
                rect.y = cY - maxY;
                rect.width = 2 * maxX;
                rect.height = 2 * maxY;
            }
//            rect = cv::Rect(0, 0, args.width, args.height);
			tmp[3](rect).copyTo(newTmp[3]);
			cv::threshold(newTmp[3], newTmp[3], 1, 255, cv::THRESH_BINARY);
			mcs.GetFrames()[i]->GetMultChannel()[2]->SetMultChannel(rect.x, rect.y, rect.width, rect.height,{newTmp[3].data}, MCType::Remap);
		}
	}
}

